package com.lilingguo.anime.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lilingguo.anime.domain.FolderPo;
import com.lilingguo.anime.domain.FolderRelPo;
import com.lilingguo.anime.mapper.FolderMapper;
import com.lilingguo.anime.service.FolderRelService;
import com.lilingguo.anime.service.FolderService;
import com.lilingguo.anime.utils.FolderUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 文件路径
 *
 * @Author: GuoZi
 * @Date: 2021/05/30 21:49
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class FolderServiceImpl extends ServiceImpl<FolderMapper, FolderPo> implements FolderService {

    private final String JEDIS_KEY = "anime:path";
    private final String INVALID_VALUE = "NULL";

    FolderMapper folderMapper;
    FolderRelService folderRelService;

    StringRedisTemplate redisTemplate;

    @Override
    public Integer saveRootFolder(FolderPo folder) {
        if (folder == null) {
            return null;
        }
        folder.setCreateTime(System.currentTimeMillis());
        folder.setRoot(true);
        boolean success = save(folder);
        if (!success || folder.getId() == null) {
            return null;
        }
        return folder.getId();
    }

    @Override
    public Integer saveFolder(FolderPo folder, Integer parentId) {
        if (folder == null || parentId == null || parentId < 1) {
            return null;
        }
        folder.setCreateTime(System.currentTimeMillis());
        folder.setRoot(false);
        save(folder);
        if (folder.getId() == null) {
            return null;
        }
        Integer fid = folder.getId();
        FolderRelPo folderRelPo = new FolderRelPo();
        folderRelPo.setChildId(fid);
        folderRelPo.setParentId(parentId);
        boolean success = folderRelService.save(folderRelPo);
        if (!success) {
            return null;
        }
        return fid;
    }

    @Override
    public List<FolderPo> findRootFolders() {
        List<FolderPo> folderList = list(Wrappers.<FolderPo>lambdaQuery().eq(FolderPo::getRoot, true));
        Collections.sort(folderList);
        return folderList;
    }

    @Override
    public List<FolderPo> findChildFoldersById(Integer fid) {
        List<FolderRelPo> list = folderRelService.list(Wrappers.<FolderRelPo>lambdaQuery().eq(FolderRelPo::getParentId, fid));
        if (ObjectUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        List<Integer> folderIdList = list.stream().map(FolderRelPo::getChildId).collect(Collectors.toList());
        List<FolderPo> folderList = listByIds(folderIdList);
        Collections.sort(folderList);
        return folderList;
    }

    @Override
    public FolderPo findParentFolder(Integer fid) {
        Integer parentId = findParentId(fid);
        if (parentId == null) {
            return null;
        }
        return getById(parentId);
    }

    @Override
    public Integer findParentId(Integer fid) {
        FolderRelPo folderRelPo = folderRelService.getOne(Wrappers.<FolderRelPo>lambdaQuery().eq(FolderRelPo::getChildId, fid));
        if (folderRelPo == null) {
            return null;
        }
        return folderRelPo.getParentId();
    }

    @Override
    public String findRelativePath(int fid) {
        if (fid < 1) {
            // @param fid is not primary key
            return null;
        }
        String key = JEDIS_KEY + ":" + fid;
        // found in redis
        String value = redisTemplate.boundValueOps(key).get();
        if (StringUtils.isNotBlank(value)) {
            if (INVALID_VALUE.equals(value)) {
                // 空值缓存
                return null;
            }
            return value;
        }
        // not found in redis
        FolderPo folder = getById(fid);
        if (folder == null || folder.getSize() < 1) {
            // save the invalid value to redis
            redisTemplate.boundValueOps(key).set(INVALID_VALUE);
            return null;
        }
        Integer childId = fid;
        List<Integer> idList = new ArrayList<Integer>();
        do {
            // query and save to list
            idList.add(childId);
            childId = findParentId(childId);
        } while (childId != null);
        List<FolderPo> folderList = listByIds(idList);

        String sub = FolderUtils.getSubString();
        StringBuilder sb = new StringBuilder();
        for (FolderPo f : folderList) {
            sb.append(sub).append(f.getName());
        }
        value = sb.toString();
        // save the relative path to redis
        redisTemplate.boundValueOps(key).set(value);
        return value;
    }

    @Override
    public boolean load() {
        return FolderUtils.load(this);
    }

    @Override
    public String findThumbnailPath(int fid) {
        String relativePath = findRelativePath(fid);
        if (relativePath == null) {
            return null;
        }
        return FolderUtils.getThumbnailPath(relativePath);
    }

    @Override
    public String findImagePath(int fid, int page) {
        String relativePath = findRelativePath(fid);
        if (relativePath == null) {
            return null;
        }
        return FolderUtils.getImagePath(relativePath, page);
    }

    @Override
    public int findTotalCount() {
        return count();
    }
}
